package com.imooc.controller;

import com.imooc.pojo.Users;
import com.imooc.pojo.vo.UserVO;
import com.imooc.service.UserService;
import com.imooc.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/**
 * @program: foodie-dev
 * @description: hello控制器
 * @author: rZ
 * @create: 2020-02-21 17:06
 **/
@ApiIgnore
@Controller
public class SSOController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisOperator redisOperator;

    public static final String REDIS_USER_TOKEN = "redis_user_token";
    public static final String REDIS_USER_TICKET = "redis_user_ticket";
    public static final String REDIS_TMP_TICKET = "redis_tmp_ticket";
    public static final String COOKIE_USER_TICKET = "cookie_user_ticket";

    @GetMapping("/login")
    public String Hello(String returnUrl, Model model,
                        HttpServletRequest request, HttpServletResponse response) {
        model.addAttribute("returnUrl", returnUrl);
        /**
         * 1.获取userTicket 门票，如果cookie中能够获取到，证明用户登录过，此时签发一个一次性临时票据
         */
        String userTicket = CookieUtils.getCookieValue(request,REDIS_USER_TICKET);
        boolean isVerified = verifyUserTicket(userTicket);
        if (isVerified){
            String tmpTicket = createTmpTicket();
            return "redirect:" + returnUrl + "?tmpTicket=" + tmpTicket;
        }
        //用户从未登录过，第一次进入则跳转到CAS的统一登录页面
        return "login";
    }

    /**
     * 校验CAS全局用户门票
     */
    private boolean verifyUserTicket(String userTicket){
        // 1.验证CAS门票不能为空
        if (StringUtils.isBlank(userTicket)){
            return false;
        }

        // 2.验证CAS门票是否有效
        String userId = redisOperator.get(REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isBlank(userId)){
            return false;
        }
        // 3.验证门票对应的user会话是否存在
        String userRedis = redisOperator.get(REDIS_USER_TOKEN + ":" + userId);
        if (StringUtils.isBlank(userRedis)){
            return false;
        }

        return true;
    }

    /**
     * CAS的统一登录接口
     * 1.登录后创建用户的全局会话 -> uniqueToken
     * 2.创建用户全局门票，用以表示在CAS端是否登录 -> userTicket
     * 3.创建用户的临时票据，用于回跳回传 -> tmpTicket
     */
    @PostMapping("/doLogin")
    public String doLogin(String username, String password, String returnUrl, Model model,
                          HttpServletRequest request, HttpServletResponse response) throws Exception {
        model.addAttribute("returnUrl", returnUrl);
        // 1.判断用户名和密码必须不能为空
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            model.addAttribute("errMsg", "用户名或密码不能为空");
            return "login";
        }

        // 2.实现登录
        Users userResult = userService.queryUserForLogin(username, MD5Utils.getMD5Str(password));
        if (userResult == null) {
            model.addAttribute("errMsg", "用户名或密码不正确");
            return "login";
        }
        // 3.实现用户的redis会话
        String uniqueToken = UUID.randomUUID().toString().trim();
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userResult, userVO);
        userVO.setUserUniqueToken(uniqueToken);
        redisOperator.set(REDIS_USER_TOKEN + ":" + userResult.getId(), JsonUtils.objectToJson(userVO));
        // 4.生成ticket门票，全局门票，代表用户在CAS端登录过
        String userTicket = UUID.randomUUID().toString().trim();
        // 4.1 用户全局门票需要放入CAS端的cookie中
        CookieUtils.setCookie(request, response, COOKIE_USER_TICKET, userTicket);
        // 5.userTicket关联用户id，并且放入到redis中，代表这个用户有门票了，可以在子系统中游玩
        redisOperator.set(REDIS_USER_TICKET + ":" + userTicket, userVO.getId());
        // 6.生成临时票据，回跳调用端网站，是由CAS端签发的
        String tmpTicket = createTmpTicket();

        /**
         * 7.userTicket：用于表示用户在CAS端的一个登录状态：已经登录
         *   tmpTicket：用于颁发给用户进行一个性的验证的票据，有时效性
         */

        /**
         * 举例：
         *      动物园门票（userTicket）：CAS系统的全局门票和用户全局绘画
         *      临时票据（tmpTicket）：由动物园门票获取园内其他景区小的临时票据，用完就销毁
         */
        return "redirect:" + returnUrl + "?tmpTicket=" + tmpTicket;
    }

    @PostMapping("/verifyTmpTicket")
    @ResponseBody
    public IMOOCJSONResult verifyTmpTicket(String tmpTicket,
                                 HttpServletRequest request, HttpServletResponse response) throws Exception {
        /**
         * 使用一次性临时票据（tmpTicket）来验证用户是否登录，如果登录过，把用户会话信息返回给站点
         * 使用完毕后，需要销毁临时票据
         */

        String tmpTicketValue = redisOperator.get(REDIS_TMP_TICKET + ":" + tmpTicket);
        if (StringUtils.isBlank(tmpTicketValue)){
            return IMOOCJSONResult.errorUserTicker("用户票据异常");
        }
        // 1.如果临时票据OK，则需要销毁，并且拿到CAS端COOKie中全局userTicket，以此再次获取用户临时票据
        if (!tmpTicketValue.equals(MD5Utils.getMD5Str(tmpTicket))){
            return IMOOCJSONResult.errorUserTicker("用户票据异常");
        }
        //销毁临时票据
        redisOperator.del(REDIS_TMP_TICKET + ":" + tmpTicket);

        // 2. 验证并且获取用户的userTicket
        String userTicket  = CookieUtils.getCookieValue(request, COOKIE_USER_TICKET);
        String userId = redisOperator.get(REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isBlank(userId)){
            return IMOOCJSONResult.errorUserTicker("用户票据异常");
        }
        // 3.验证门票对应的user会话是否存在
        String userRedis = redisOperator.get(REDIS_USER_TOKEN + ":" + userId);
        if (StringUtils.isBlank(userRedis)){
            return IMOOCJSONResult.errorUserTicker("用户票据异常");
        }
        // 4.验证成功，返回OK,携带用户会话。
        return IMOOCJSONResult.ok(JsonUtils.jsonToPojo(userRedis,UserVO.class));
    }

    @PostMapping("/logout")
    @ResponseBody
    public IMOOCJSONResult logout(String userId,
                                           HttpServletRequest request, HttpServletResponse response)  {
        //获取cas的用户门票
        String userTicket  = CookieUtils.getCookieValue(request, COOKIE_USER_TICKET);
        //清除userTicket票据 redis/cookie
        CookieUtils.deleteCookie(request,response,COOKIE_USER_TICKET);
        redisOperator.del(REDIS_USER_TICKET + ":" + userTicket);

        // 3.清除用户全局会话（分布式会话）
        redisOperator.del(REDIS_USER_TOKEN + ":" + userId);

        return IMOOCJSONResult.ok("退出成功");
    }

    /**
     * 创建临时票据
     *
     * @return
     */
    private String createTmpTicket() {
        String tmpTicket = UUID.randomUUID().toString().trim();
        try {
            redisOperator.set(REDIS_TMP_TICKET + ":" + tmpTicket, MD5Utils.getMD5Str(tmpTicket), 600);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return tmpTicket;
    }

}
